package com.ruihu.admin.module.support.quartz.task;

import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.databind.JsonNode;
import com.meritit.ws.intfsservicews.IntfsServiceWS;
import com.meritit.ws.intfsservicews.IntfsServiceWS_Service;
import com.meritit.ws.intfsservicews.MDMOrganization;
import com.meritit.ws.intfsservicews.MDMUser;
import com.ruihu.admin.common.constant.GlobalConst;
import com.ruihu.admin.common.domain.ITask;
import com.ruihu.admin.common.domain.ResponseDTO;
import com.ruihu.admin.module.system.department.DepartmentService;
import com.ruihu.admin.module.system.department.DepartmentTreeService;
import com.ruihu.admin.module.system.department.domain.dto.DepartmentCreateDTO;
import com.ruihu.admin.module.system.department.domain.dto.DepartmentUpdateDTO;
import com.ruihu.admin.module.system.department.domain.vo.DepartmentVO;
import com.ruihu.admin.module.system.org.domain.dto.OrgAddDTO;
import com.ruihu.admin.module.system.org.domain.dto.OrgUpdateDTO;
import com.ruihu.admin.module.system.org.domain.vo.OrgVO;
import com.ruihu.admin.module.system.org.service.OrgService;
import com.ruihu.admin.module.system.role.basic.RoleService;
import com.ruihu.admin.module.system.role.basic.domain.dto.RoleVO;
import com.ruihu.admin.module.system.user.UserService;
import com.ruihu.admin.module.system.user.constant.UserStatusEnum;
import com.ruihu.admin.module.system.user.constant.UserTypeEnum;
import com.ruihu.admin.module.system.user.domain.dto.UserAddDTO;
import com.ruihu.admin.module.system.user.domain.dto.UserDTO;
import com.ruihu.admin.module.system.user.domain.dto.UserUpdateDTO;
import com.ruihu.admin.util.BeanCopierUtil;
import com.ruihu.admin.util.RhJsonUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.net.MalformedURLException;
import java.net.URL;
import java.util.*;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 同步用户组织信息任务
 */
@Slf4j
@Component("syncUserInfoTask")
public class SyncUserInfoTask implements ITask {

    private static final String TOP_ORGANIZATION_CODE = "00000000";

    /**
     * 全量同步
     */
    private static final String SYNC_FULL = "full";

    /**
     * 增量同步
     */
    private static final String SYNC_INCR = "incr";

    /**
     * 法人公司编码
     */
    private static String[] LEGAL_NO = new String[] {
            "C01", "C02", "C03", "C04", "C05", "C06", "C07", "C08", "C09",
            "C10", "C11", "C12", "C13", "C14", "C15", "C16", "C17", "C18",
            "C19", "C20", "C21", "C22", "C23", "C24", "C25", "C26", "C27",
            "C28", "C29", "C30", "C31", "C32", "C33", "C34", "C35", "C36",
            "C37", "C38"
    };

    private static String ROLE_NAME = "操作工";

    private static final ReentrantLock lock = new ReentrantLock();

    @Value("${mdm.webservice.url}")
    private String url;

    private IntfsServiceWS intfsServiceWS;

    @Autowired
    private OrgService orgService;

    @Autowired
    private UserService userService;

    @Autowired
    private RoleService roleService;

    @Autowired
    private DepartmentService departmentService;

    @Autowired
    private DepartmentTreeService departmentTreeService;


    public SyncUserInfoTask() {
    }

    @Override
    public void execute(String paramJson) throws Exception {
        Map<String, String> param = RhJsonUtil.parseJson(paramJson, Map.class);
        String syncMode;   // 同步模式，incr增量
        if (SYNC_FULL.equals(param.get("syncMode")))
            syncMode = SYNC_FULL;
        else
            syncMode = SYNC_INCR;

        // 初始化WebService服务
        this.initWebService();
        // 获取MDM业务组织
        List<MDMOrganization> mdmOrganizationList = this.getMDMOrganizationList(syncMode);
        Map<String, MDMOrganization> mdmOrganizationMap = mdmOrganizationList.stream()
                .collect(Collectors.toMap(MDMOrganization::getOrganizationCode, Function.identity()));
        // 获取MDM用户数据
        List<MDMUser> mdmUserList = this.getMDMUserList(syncMode);
        Map<String, MDMUser> mdmUserMap = mdmUserList.stream().collect(Collectors.toMap(MDMUser::getEmpCode, Function.identity()));
        // 获取系统机构数据
        ResponseDTO<List<OrgVO>> orgResp = this.orgService.listAll();
        MDMOrganization topMDMOrganization = mdmOrganizationMap.get(TOP_ORGANIZATION_CODE);
        if (orgResp.isSuccess()) {
            Map<String, OrgVO> existOrgMap = orgResp.getData().stream().collect(Collectors.toMap(OrgVO::getOrgCode, Function.identity()));
            topMDMOrganization = this.saveOrganizations(topMDMOrganization, existOrgMap);
        }
        // 获取系统部门数据
        ResponseDTO<List<DepartmentVO>> departmentResp = this.departmentService.listAllActive();
        Map<String, DepartmentVO> existDepartmentMap = departmentResp.getData().stream()
                .collect(Collectors.toMap(DepartmentVO::getDepartmentCode, Function.identity()));
        // 获取系统用户数据
        List<UserDTO> existUserList = this.userService.getAllUser2();
        Map<String, UserDTO> existUserVOMap = existUserList.stream().collect(Collectors.toMap(UserDTO::getUsername, Function.identity()));
        // 同步部门
        this.saveDepartments(mdmOrganizationMap, topMDMOrganization, existDepartmentMap, existUserVOMap, mdmUserMap);
        List<DepartmentVO> departmentVOList = this.departmentService.listAll().getData();
        // 更新部门上级部门列表
        this.updateDepartmentParentDepartmentNames(departmentVOList, mdmOrganizationList);
        // 同步人员
        this.saveUsers(mdmUserList, existUserVOMap, departmentVOList);
    }

    /**
     * 更新部门的上级部门列表
     * @param departmentVOList
     * @param mdmOrganizationList
     */
    private void updateDepartmentParentDepartmentNames(List<DepartmentVO> departmentVOList, List<MDMOrganization> mdmOrganizationList) {
        this.departmentTreeService.buildTree(departmentVOList);
        for (MDMOrganization mdmOrganization : mdmOrganizationList) {
            Optional<DepartmentVO> optional = departmentVOList.stream()
                    .filter(e -> e.getId().equals(mdmOrganization.getId()))
                    .findFirst();
            if (optional.isPresent()) {
                DepartmentUpdateDTO updateDTO = BeanCopierUtil.copy(optional.get(), DepartmentUpdateDTO.class);
                this.departmentService.updateDepartmentSimple(updateDTO);
            }
        }
    }

    /**
     * 同步人员
     */
    private void saveUsers(List<MDMUser> mdmUserList, Map<String, UserDTO> existUserVOMap, List<DepartmentVO> departmentVOList) {
        Map<String, DepartmentVO> departmentVOMap = departmentVOList
                .stream()
                .filter(e -> StrUtil.isNotEmpty(e.getDepartmentCode()))
                .collect(Collectors.toMap(DepartmentVO::getDepartmentCode, Function.identity()));
        Long roleId = null;
        ResponseDTO<RoleVO> roleVOResp = this.roleService.getRoleByName(ROLE_NAME);
        if (roleVOResp.isSuccess())
            roleId = roleVOResp.getData().getId();
        for (MDMUser mdmUser : mdmUserList) {
            try {
                if (existUserVOMap.containsKey(mdmUser.getEmpCode())) {
                    // 更新用户
                    UserDTO userDTO = existUserVOMap.get(mdmUser.getEmpCode());
                    userDTO.setActualName(mdmUser.getEmpName());
                    userDTO.setPhone(mdmUser.getMobilePhone());
                    userDTO.setIsLeave(Integer.valueOf(0).equals(mdmUser.getStatus()) ? 1 : 0);
                    // 判断部门是否变化
                    DepartmentVO mdmUserDepartment = departmentVOMap.get(mdmUser.getOrganizationCode());
                    if (Objects.nonNull(mdmUserDepartment))
                        userDTO.setDepartmentId(mdmUserDepartment.getId());
                    UserUpdateDTO userUpdateDTO = BeanCopierUtil.copy(userDTO, UserUpdateDTO.class);
                    userUpdateDTO.setRoleId(roleId);
                    this.userService.updateUserSimple(userUpdateDTO);
                } else {    // 新增用户
                    UserAddDTO userAddDTO = new UserAddDTO();
                    userAddDTO.setUsername(mdmUser.getEmpCode());
                    userAddDTO.setWorkNo(mdmUser.getEmpCode());
                    userAddDTO.setActualName(mdmUser.getEmpName());
                    userAddDTO.setNickName(mdmUser.getEmpName());
                    userAddDTO.setUserType(UserTypeEnum.NORMAL_USER.getValue());
                    userAddDTO.setPhone(mdmUser.getMobilePhone());
                    userAddDTO.setPassword(null);
                    userAddDTO.setRoleId(roleId);

                    DepartmentVO departmentVO = departmentVOMap.get(mdmUser.getOrganizationCode());
                    if (Objects.nonNull(departmentVO))
                        userAddDTO.setDepartmentId(departmentVO.getId());
                    else {
                        log.warn("User department not exists, user code {}, department code {}", mdmUser.getEmpCode(), mdmUser.getOrganizationCode());
                        userAddDTO.setDepartmentId(0L);
                    }
                    userAddDTO.setIsLeave(UserStatusEnum.NORMAL.getValue());
                    this.userService.addUserSimple(userAddDTO, null);
                }
            } catch (Exception ex) {
                log.error(ex.getMessage(), ex);
            }
        }
    }

    /**
     * 获取MDM人员数据
     * @return
     */
    private List<MDMUser> getMDMUserList(String syncMode) {
        List<MDMUser> mdmUserList = new LinkedList<>();
        try {
            if (SYNC_FULL.equals(syncMode)) {
                String queryCondition = "";
                for (String legalNo : LEGAL_NO) {
                    queryCondition = "VERSIONSTATUS:=1;CORPORATE_ORGANIZATION_CODE=" + legalNo;
                    String queryField = "EMP_CODE,EMP_NAME,MOBILEPHONE,ORGANIZATION_CODE,STATUS";
                    String result = this.intfsServiceWS.getModelDatas(GlobalConst.MDMInterface.USER_MODEL_CODE,
                            queryCondition, queryField,
                            GlobalConst.MDMInterface.DATA_TYPE, "YES",
                            GlobalConst.SYS_SHORT_NAME, "");
                    if (StrUtil.isNotEmpty(result)) {
                        JsonNode jsonNode = RhJsonUtil.objectMapper().readTree(result);
                        JsonNode listNode = jsonNode.at("/LIST");
                        for (int i = 0; i < listNode.size(); i++) {
                            MDMUser mdmUser= RhJsonUtil.convertValue(listNode.get(i).at("/SVOLT_RY"), MDMUser.class);
                            mdmUserList.add(mdmUser);
                        }
                    }
                }
            } else if (SYNC_INCR.equals(syncMode)) {
                Date now = new Date();
                String sValidateDate = DateUtil.formatDate(now) + " 00:00:00";
                String eValidateDate = DateUtil.formatDateTime(now) + " 23:59:59";
                String queryCondition = "VERSIONSTATUS:=1;S_VALIDDATE:=" + sValidateDate + ";E_VALIDDATE:=" + eValidateDate;
                String result = this.intfsServiceWS.getModelDatas(GlobalConst.MDMInterface.USER_MODEL_CODE,
                        queryCondition, "",
                        GlobalConst.MDMInterface.DATA_TYPE, "YES",
                        GlobalConst.SYS_SHORT_NAME, "");

                if (StrUtil.isNotEmpty(result)) {
                    JsonNode jsonNode = RhJsonUtil.objectMapper().readTree(result);
                    JsonNode listNode = jsonNode.at("/LIST");
                    for (int i = 0; i < listNode.size(); i++) {
                        MDMUser mdmUser= RhJsonUtil.convertValue(listNode.get(i).at("/SVOLT_RY"), MDMUser.class);
                        mdmUserList.add(mdmUser);
                    }
                }
            }
        } catch (Exception ex) {
            log.error(String.format("Get user info from MDM error, %s", ex.getMessage()), ex);
        }

        return mdmUserList;
    }

    /**
     * 保存组织机构
     * @param topMDMOrg
     * @param existOrgMap
     */
    private MDMOrganization saveOrganizations(MDMOrganization topMDMOrg, Map<String, OrgVO> existOrgMap) {
        if (Objects.nonNull(topMDMOrg) && TOP_ORGANIZATION_CODE.equals(topMDMOrg.getOrganizationCode())) {
            if (existOrgMap.containsKey(TOP_ORGANIZATION_CODE)) {
                // 更新
                OrgVO orgVO = existOrgMap.get(TOP_ORGANIZATION_CODE);
                topMDMOrg.setId(orgVO.getId());
                if (StrUtil.compare(topMDMOrg.getOrganizationName(), orgVO.getOrgName(), true) != 0) {
                    OrgUpdateDTO orgUpdateDTO = BeanCopierUtil.copy(orgVO, OrgUpdateDTO.class);
                    orgUpdateDTO.setOrgName(topMDMOrg.getOrganizationName());
                    this.orgService.update(orgUpdateDTO);
                }
            } else {
                // 新增
                OrgAddDTO orgAddDTO = new OrgAddDTO();
                orgAddDTO.setOrgCode(topMDMOrg.getOrganizationCode());
                orgAddDTO.setOrgName(topMDMOrg.getOrganizationName());
                orgAddDTO.setCreateTime(new Date());
                orgAddDTO.setParentId(0L);
                ResponseDTO<Long> addResp = this.orgService.add(orgAddDTO);
                if (addResp.isSuccess())
                    topMDMOrg.setId(addResp.getData());
            }
        } else {
            OrgVO orgVO = existOrgMap.get(TOP_ORGANIZATION_CODE);
            topMDMOrg = new MDMOrganization();
            topMDMOrg.setId(orgVO.getId());
            topMDMOrg.setOrganizationCode(orgVO.getOrgCode());
            topMDMOrg.setOrganizationName(orgVO.getOrgName());
            topMDMOrg.setShortName(orgVO.getOrgName());
        }

        return topMDMOrg;
    }

    /**
     * 保存部门
     * @param mdmOrganizationMap
     * @param existDepartmentMap
     */
    private void saveDepartments(Map<String, MDMOrganization> mdmOrganizationMap,
                                 MDMOrganization topMDMOrganization,
                                 Map<String, DepartmentVO> existDepartmentMap,
                                 Map<String, UserDTO> existUserVOMap,
                                 Map<String, MDMUser> mdmUserMap) {
        List<MDMOrganization> newMDMDepartmentList = new LinkedList<>();
        List<MDMOrganization> updateMDMDepartmentList = new LinkedList<>();
        for (Map.Entry<String, MDMOrganization> mdmOrgEntry : mdmOrganizationMap.entrySet()) {
            MDMOrganization mdmOrganization = mdmOrgEntry.getValue();
            mdmOrganization.setTopOrganizationId(topMDMOrganization.getId());
            if (existDepartmentMap.containsKey(mdmOrganization.getOrganizationCode()))
                updateMDMDepartmentList.add(mdmOrganization);
            else
                newMDMDepartmentList.add(mdmOrganization);
        }
        newMDMDepartmentList.forEach(mdmOrganization -> this.saveNewDepartment(mdmOrganization,mdmOrganizationMap,existDepartmentMap,existUserVOMap,mdmUserMap));

        ResponseDTO<List<DepartmentVO>> departmentResp = this.departmentService.listAllActive();
        Map<String, DepartmentVO> existDepartmentMap2 = departmentResp.getData().stream().collect(Collectors.toMap(DepartmentVO::getDepartmentCode, Function.identity()));
        updateMDMDepartmentList.forEach(mdmOrganization -> this.updateDepartment(mdmOrganization, existDepartmentMap2, existUserVOMap, mdmUserMap));
    }

    private Long saveNewDepartment(MDMOrganization mdmOrganization,
                                   Map<String, MDMOrganization> mdmOrganizationMap,
                                   Map<String, DepartmentVO> existDepartmentMap,
                                   Map<String, UserDTO> existUserVOMap,
                                   Map<String, MDMUser> mdmUserMap) {
        if (!mdmOrganization.isDisposed()) {
            if (!TOP_ORGANIZATION_CODE.equals(mdmOrganization.getOrganizationCode())) {
                DepartmentCreateDTO createDTO = new DepartmentCreateDTO();
                createDTO.setOrgId(mdmOrganization.getTopOrganizationId());
                createDTO.setDepartmentCode(mdmOrganization.getOrganizationCode());
                createDTO.setDepartmentName(mdmOrganization.getOrganizationName());
                createDTO.setDepartmentShortName(mdmOrganization.getShortName());
                createDTO.setManagerCode(mdmOrganization.getOrganizationManager());
                String managerName = this.getDepartmentManagerName(mdmOrganization.getOrganizationManager(), existUserVOMap, mdmUserMap);
                createDTO.setManagerName(managerName);

                MDMOrganization parentOrganization = mdmOrganizationMap.get(mdmOrganization.getParentOrganizationCode());
                if (Objects.nonNull(parentOrganization)
                        && !TOP_ORGANIZATION_CODE.equals(parentOrganization.getOrganizationCode())) {
                    if (Objects.isNull(parentOrganization.getId())) {
                        Long parentId = this.saveNewDepartment(parentOrganization, mdmOrganizationMap, existDepartmentMap, existUserVOMap, mdmUserMap);
                        parentOrganization.setId(parentId);
                        createDTO.setParentId(parentId);
                    } else {
                        createDTO.setParentId(parentOrganization.getId());
                    }
                } else if (Objects.isNull(parentOrganization)) {
                    DepartmentVO existDepartmentVO = existDepartmentMap.get(parentOrganization.getOrganizationCode());
                    if (Objects.nonNull(existDepartmentVO)) {
                        createDTO.setParentId(existDepartmentVO.getId());
                    }
                }
                ResponseDTO<Long> addResp = this.departmentService.addDepartmentSimple(createDTO);
                if (addResp.isSuccess()) {
                    mdmOrganization.setId(addResp.getData());
                }
            }

            // 设定disposed处理过标志，避免往上处理父部门时可能会重复处理问题
            mdmOrganization.setDisposed(true);
        }
        return mdmOrganization.getId();
    }

    private void updateDepartment(MDMOrganization mdmOrganization,
                                  Map<String, DepartmentVO> existDepartmentMap,
                                  Map<String, UserDTO> existUserVOMap,
                                  Map<String, MDMUser> mdmUserMap) {
        DepartmentVO departmentVO = existDepartmentMap.get(mdmOrganization.getOrganizationCode());
        if (Objects.nonNull(departmentVO)) {
            DepartmentUpdateDTO updateDTO = BeanCopierUtil.copy(departmentVO, DepartmentUpdateDTO.class);

            updateDTO.setOrgId(mdmOrganization.getTopOrganizationId());
            updateDTO.setDepartmentCode(mdmOrganization.getOrganizationCode());
            updateDTO.setDepartmentName(mdmOrganization.getOrganizationName());
            updateDTO.setDepartmentShortName(mdmOrganization.getShortName());
            updateDTO.setManagerCode(mdmOrganization.getOrganizationManager());
            String managerName = this.getDepartmentManagerName(mdmOrganization.getOrganizationManager(), existUserVOMap, mdmUserMap);
            updateDTO.setManagerName(managerName);

            DepartmentVO mdmParentDepartmentVO = existDepartmentMap.get(mdmOrganization.getParentOrganizationCode());
            if (Objects.nonNull(mdmParentDepartmentVO)) {
                updateDTO.setParentId(mdmParentDepartmentVO.getId());
            }
            this.departmentService.updateDepartmentSimple(updateDTO);
            // 设置ID
            mdmOrganization.setId(departmentVO.getId());
        }
    }

    /**
     * 获取部门管理者姓名
     * @param existUserVOMap
     * @param mdmUserMap
     * @return
     */
    private String getDepartmentManagerName(String managerCode, Map<String, UserDTO> existUserVOMap, Map<String, MDMUser> mdmUserMap) {
        if (mdmUserMap.containsKey(managerCode))
            return mdmUserMap.get(managerCode).getEmpName();
        if (existUserVOMap.containsKey(managerCode))
            return existUserVOMap.get(managerCode).getActualName();

        return null;
    }

    private void initWebService() {
        lock.lock();
        try {
            if (this.intfsServiceWS == null) {
               URL url = new URL(this.url);
               IntfsServiceWS_Service serviceImpl = new IntfsServiceWS_Service(url);
               this.intfsServiceWS = serviceImpl.getIntfsServiceWSPort();
            }
        } catch (MalformedURLException e) {
            log.error(e.getMessage(), e);
        } finally {
            lock.unlock();
        }
    }

    /**
     * 获取MDM业务组织数据
     * @param syncMode
     * @return
     */
    private List<MDMOrganization> getMDMOrganizationList(String syncMode) {
        try {
            String queryCondition = "";
            if (SYNC_FULL.equals(syncMode)) {
                queryCondition = "VERSIONSTATUS:=1";
            } else {
                Date now = new Date();
                String sValidateDate = DateUtil.formatDate(now) + " 00:00:00";
                String eValidateDate = DateUtil.formatDateTime(now) + " 23:59:59";
                queryCondition = "VERSIONSTATUS:=1;S_VALIDDATE:=" + sValidateDate + ";E_VALIDDATE:=" + eValidateDate;
            }
            String queryField = "ORGANIZATION_CODE,ORGANIZATION_NAME,SHORT_NAME,PARENT_ORGANIZATION_CODE,ORGANIZATION_MANAGER";
            String result = this.intfsServiceWS.getModelDatas(GlobalConst.MDMInterface.ORG_MODEL_CODE,
                    queryCondition, queryField,
                    GlobalConst.MDMInterface.DATA_TYPE, "YES",
                    GlobalConst.SYS_SHORT_NAME, "");
            if (StrUtil.isNotEmpty(result)) {
                JsonNode jsonNode = RhJsonUtil.objectMapper().readTree(result);
                JsonNode listNode = jsonNode.at("/LIST");
                List<MDMOrganization> organizationList = new ArrayList<>(listNode.size());
                for (int i = 0; i < listNode.size(); i++) {
                    MDMOrganization mdmOrganization= RhJsonUtil.convertValue(listNode.get(i).at("/SVOLT_ZZ_YWZZ"), MDMOrganization.class);
                    organizationList.add(mdmOrganization);
                }
                return organizationList;
            }
        } catch (Exception ex) {
            log.error(String.format("Get organization info from MDM error, %s", ex.getMessage()), ex);
        }

        return ListUtil.empty();
    }
}
